import os
import re
import time
import json
import simplejson
import requests
from api.base import *
from api.folder import *
from common.logger import logger
from common.retcode import RetCode, TransferStatus
from common.types import FileInfo, PathInfo, FolderTree
from common.models import FileList, PathList, TreeList
from common.utils import get_chunk_size

class DownloadAction(FolderAction):
	def __init__(self, sess_wp: SessionWrapper):
		super(DownloadAction, self).__init__(sess_wp)

	def _down_one_link(self, transfer_info, durl, callback=None) -> int:
		"""下载器"""
		if not os.path.exists(transfer_info.path):
			os.makedirs(transfer_info.path)
		os.environ['LANG'] = 'enUS.UTF-8'
		resp = self._get(durl, stream=True, timeout=None)
		if not resp:
			# logger.error("Download link: network error!")
			return RetCode.FAILED, '因网络原因无法下载'

		content_d = resp.headers['content-disposition'].encode('latin-1').decode('utf-8')
		file_name = re.search(r'filename="(.+)"', content_d)
		file_name = file_name.group(1) if file_name else ''
		if not file_name:
			# logger.error("Download link: cannot get file name!")
			return RetCode.FAILED, '无法获得该文件的名称，请重试'

		file_path = transfer_info.path + os.sep + file_name

		total_size = resp.headers.get('content-length')
		if total_size:
			total_size = int(total_size)
		else:  # no content length in headers
			total_size = -1 # 打包文件夹下载时，返回的total_size为-1

		# 是新加入的任务而不是旧任务的恢复，需要检查是否有文件存在，如果存在则重命名
		# 另外，文件夹下载，不能断点续传，也需要重来
		if transfer_info.status == TransferStatus.WAITING and os.path.exists(file_path):
			org_file_name = file_name
			suffix_pos = org_file_name.rfind('.')
			timestamp = int(time.time()*1000)
			while True:
				if suffix_pos == -1:
					file_name = org_file_name + '-' + str(timestamp)
				else:
					file_name = org_file_name[0:suffix_pos] + '-' + str(timestamp) + org_file_name[suffix_pos:]
				file_path = transfer_info.path + os.sep + file_name
				if os.path.exists(file_path):
					timestamp += 1
				else:
					break
		elif total_size == -1 and transfer_info.status == TransferStatus.RESUMING and os.path.exists(file_path):
			os.remove(file_path)
			transfer_info.nowSize = 0

		transfer_info.name = file_name
		transfer_info.totalSize = total_size
		transfer_info.last_tick = time.perf_counter()

		now_size = 0
		if os.path.exists(file_path) and total_size != -1:
			now_size = os.path.getsize(file_path)  # 本地已经下载的文件大小
			if now_size >= total_size:  # 已经下载完成
				if callback is not None:
					callback(transfer_info, now_size)
				# logger.debug(f"Download link: the file already exists in the local, file_name={file_name}, durl={durl}")
				return RetCode.SUCCESS, f'该文件已经存在于本地磁盘中：{file_name}，{durl}'
			else:  # 断点续传
				headers = {**self._headers, 'Range': f'bytes={now_size}-'}
				resp = self._get(durl, stream=True, headers=headers, timeout=None)
				if not resp:
					return RetCode.FAILED, '断点续传失败'

		logger.debug(f'Download link: file_path={file_path}, now_size={now_size}, total_size={total_size}')
		chunk_size = get_chunk_size(total_size)
		to_continue = True
		with open(file_path, "ab") as f:
			for chunk in resp.iter_content(chunk_size):
				if chunk:
					f.write(chunk)
					f.flush()
					now_size += len(chunk)
					if callback is not None:
						to_continue = callback(transfer_info, now_size)
						if not to_continue:
							break
		if to_continue:
			if (total_size == -1) and (callback is not None):
				transfer_info.totalSize = now_size
				callback(transfer_info, now_size)
			logger.debug(f"Download link: finished, total_size={total_size}, now_size={now_size}")
		else:
			logger.debug(f"Download link: breaked, total_size={total_size}, now_size={now_size}")
		return RetCode.SUCCESS, ''

	def down_file_by_id(self, transfer_info, callback=None) -> int:
		"""通过 fid 下载单个文件"""
		code, infos = self.get_file_info_by_id(transfer_info.fid)
		if code != RetCode.SUCCESS:
			return code, f"获取文件fid={transfer_info.fid}详情失败！"
		durl = 'https:' + infos.durl
		return self._down_one_link(transfer_info, durl, callback)

	def down_dirzip_by_id(self, transfer_info, callback=None) -> int:
		"""打包下载文件夹"""
		url = self._host_url + '/downloadMultiFiles.action'
		params = {
			'fileIdS': transfer_info.fid,
			'downloadType': 1,
			'recursive': 1
		}
		resp = self._get(url, params=params, allow_redirects=False)
		if resp.status_code == requests.codes['found']:  # 302
			durl = resp.headers.get("Location")
		else:
			return RetCode.FAILED, f"下载文件夹失败: {resp.status_code}"

		return self._down_one_link(transfer_info, durl, callback)